package com.tom.architect.party5.day38.simple7.base;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

/**
 * Added by Tom on 2024/09/30.
 */
public class BasePresenter<V extends BaseView, M extends BaseModel> {
    // 目前2个共用方法，传递的时候会有不同的View。不是固定的。使用基类+ 泛型 来解决。
    private V mView;
    private V mProxyView; // 代理的View
    // View的特点：必须都是接口，才能使用动态代理
    // mView一般是Activity，涉及到内存泄漏。已经解绑了，不会泄漏。可以不使用软引用，不过最好还是使用弱引用。
    private M mModel; // 动态创建的model对象。声明泛型M。一对一。

    public void attach(V view) {
        this.mView = view; // 绑定view，真正执行的View
        // 用代理对象，动态代理优化。
        this.mProxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(),
                view.getClass().getInterfaces(), // 获取接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 执行这个方法，实际是调用被代理的对象（Fragment或Activity）。
                        // 没解绑，执行的是原始的被代理View的方法。
                        if (mView == null) return null;
                        return method.invoke(mView, args);
                    }
                });

        // 动态的创建 Model。反射获取clazz
        // 获取类上面的class
        Type genType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        try {
            // TODO 判断一下类型，拿到泛型上的第一个位置。UserInfoModel
            mModel = (M) ((Class) params[1]).newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    public void detach() {
        // 不解绑，会导致内存泄漏：Activity 持有Presenter；Presenter持有Activity。
        this.mView = null;
        // this.mProxyView = null; // TODO，此处注释掉
    }

    // 返回代理的view
    public V getView() {
        return mProxyView;
    }

    public M getModel() {
        return mModel;
    }
}
